/* Author: Mark Faust;   
 * Description: This file defines the two required functions for the branch predictor.
 */

#include "predictor.h"
#include <cstdlib>

int Perceptrons[ NUM_PERCEPTRONS ][ NUM_WEIGHTS ] = { 0 };
int BranchHistory[ HISTORY_LENGTH ] = { 0 };
//int BranchHistory[ HISTORY_LENGTH ] = { 1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1,-1,-1,1 };

unsigned int branch_address = 0;
int index_p = 0;
int prediction = 0;
unsigned int global_history = 0;

bool PREDICTOR::get_prediction( const branch_record_c* br, const op_state_c* os )
{
    branch_address = br->instruction_addr;
    
    index_p = getPerceptronIndex();
    
    prediction = getPrediction(); 
            
    if( prediction >= 0 )
        return TAKEN;
    else
        return NOT_TAKEN;
}    
 
 
// Update the predictor after a prediction has been made.  This should accept
// the branch record (br) and architectural state (os), as well as a third
// argument (taken) indicating whether or not the branch was taken.
void PREDICTOR::update_predictor( const branch_record_c* br, const op_state_c* os, bool taken )
{
    bool prediction_sign;
    int t;
    
    if( prediction >= 0 )
        prediction_sign = true;
    else
        prediction_sign = false;

    //if( br->is_conditional )
	    global_history <<= 1;

    if( taken )
    {
	   // if( br->is_conditional )
	    global_history |= GLOBAL_HISTORY_UPDATE_T;
        t = 1;
    }
    else
    {	        
	//if( br->is_conditional )
	    global_history &= GLOBAL_HISTORY_UPDATE_N;
        t = -1;
    }

	    global_history &= GLOBAL_HISTORY_MASK;

	    
    if( ( prediction_sign != taken ) ||  ( abs( prediction ) <= PHI ) )
    {
        for( int i = 0; i < NUM_WEIGHTS; ++i )
            Perceptrons[ index_p ][ i ] = Perceptrons[ index_p ][ i ] + ( t * BranchHistory[ i ] );
    }
    
    for( int i = 0; i < HISTORY_LENGTH - 1; ++i )
        BranchHistory[ i ] = BranchHistory[ i + 1 ];
        
    BranchHistory[ HISTORY_LENGTH - 1 ] = t;

    /* replace this code with your own */
    /*    bool prediction = false;

    printf("%0x %0x %1d %1d %1d %1d ",br->instruction_addr,
                                      br->branch_target,br->is_indirect,br->is_conditional,
                                      br->is_call,br->is_return);
    if( br->is_conditional )
        prediction = true;
    return prediction;   // true for taken, false for not taken
    */
}

int getPerceptronIndex()
{
	
	unsigned int global_historyu = global_history << 4 ;
	global_historyu &= ~GLOBAL_HISTORY_MASK;

	//unsigned int branch_addressu = (branch_address*PRIME_NUM) & ~GLOBAL_HISTORY_MASK;
	unsigned int branch_addressu = (branch_address) & ~GLOBAL_HISTORY_MASK;
	branch_addressu ^= global_historyu;

	unsigned int branch_addressl = branch_address & GLOBAL_HISTORY_MASK;
	branch_addressl ^= global_history;

	/*
	unsigned int global_historyu = global_history << 12 ;
	global_historyu &= ~GLOBAL_HISTORY_MASK;

	unsigned int branch_address_retainu = global_history & GLOBAL_HISTORY_MASKRU;
       	unsigned int branch_address_retainl = global_history & GLOBAL_HISTORY_MASKRL;

	unsigned int branch_addressu = (branch_address*PRIME_NUM) & ~GLOBAL_HISTORY_MASK;
	branch_addressu ^= global_historyu;
	branch_addressu &= ~GLOBAL_HISTORY_MASKRU;

	unsigned int branch_addressl = branch_address & GLOBAL_HISTORY_MASK;
	branch_addressl ^= global_history;
	branch_addressl &= ~GLOBAL_HISTORY_MASKRL;
	*/
/*
    unsigned long int mask = 0x3FF;
    unsigned long int PC_31to10 = branch_address >> 10;
    unsigned long int PC_9to2 = (( branch_address >> 2 ) & mask );
    
    return (( PRIME_NUM * PC_31to10 ) + PC_9to2 ) % NUM_PERCEPTRONS;
*/
	//return abs(branch_address % NUM_PERCEPTRONS);
	//return branch_address % NUM_PERCEPTRONS;

	return ((branch_addressu | branch_addressl) % NUM_PERCEPTRONS);

	//return ((branch_addressu | branch_address_retainu | branch_address_retainl | branch_addressl) % NUM_PERCEPTRONS);
}

int getPrediction()
{
    int prediction_guess = 0;
    
    for( int i = 1; i < NUM_WEIGHTS; ++i )
        prediction_guess = prediction_guess + ( BranchHistory[ i ] * Perceptrons[ index_p ][ i ] );
        
    prediction_guess = prediction_guess + Perceptrons[ index_p ][ 0 ];
    
    return prediction_guess;
}
